//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#include "rs/reflection/Class.hpp"
#include "rs/Assert.hpp"
#include <cstring>
#include <algorithm>

namespace rs
{
    namespace reflection
    {   
        namespace
        {
            template< typename T >
            Boolean Compare(T const& lhs, Char8 const* rhs) {
                return std::strcmp(lhs.GetName(), rhs) < 0;
            }
        }
        
        Attribute const* Class::GetAttributeViaName(Char8 const* name) const {
            Attribute const* result = std::lower_bound(attributes_, attributes_ + attributeCount_, name, &Compare< Attribute >);
            if (result != attributes_ + attributeCount_) {
                if (std::strcmp(result->GetName(), name) == 0) {
                    return result;
                }
            }
            
            return 0;
        }
        
        Method const& Class::GetMethodViaName(Char const* name, Int32 argumentCount, Type const* argumentTypes) const {
            Method const* result = std::lower_bound(methods_, methods_ + methodCount_, name, &Compare< Method >);
            if (result != methods_ + methodCount_) {                
                while (result) {
                    if (std::strcmp(result->GetName(), name) == 0) {
                        if (argumentCount >= 0) {
                            if (result->GetArgumentCount() != static_cast< UInt32 >(argumentCount)) {
                                ++result;
                                continue;
                            }
                            
                            if (argumentTypes != 0) {
                                Int32 index = 0;
                                while (index < argumentCount) {
                                    if (!CompareArgumentTypes(result->GetArgumentType(index).ToIntegral(), (argumentTypes + index)->ToIntegral())) {
                                        ++result;
                                        break;
                                    }
                                    
                                    ++index;
                                }
                                
                                return *result;
                            }
                            else {
                                return *result;
                            }
                        }
                        else {
                            return *result;
                        }
                    }
                    else {
                        result = 0;
                    }
                }
            }
            
            RS_ASSERTF(false, "Could not find method '%s'", name);
            return *(new Method);
        }
        
        Boolean Class::Equals(Class const& classRecord) const {
            return (name_ == classRecord.GetName()) || (std::strcmp(name_, classRecord.GetName()) == 0);
        }
        
        Boolean Class::Equals(Char8 const* className) const {
            RS_ASSERT(className != 0, "className is null");
            return (name_ == className) || std::strcmp(name_, className);
        }
        
        Boolean Class::IsSubclass(Class const& classRecord) const {
            Class const* currentClass = this;
            while (currentClass) {
                if (currentClass->Equals(classRecord)) {
                    return true;
                }
                
                currentClass = currentClass->parent_;
            }
            
            return false;
        }
        
        Boolean Class::IsSubclass(Char8 const* className) const {
            Class const* currentClass = this;
            while (currentClass) {
                if (currentClass->Equals(className)) {
                    return true;
                }
                
                currentClass = currentClass->parent_;
            }
            
            return false;
        }
        
        Boolean Class::ConstructImpl(std::vector< Constructor > const& constructors, UInt64* argumentTypes, UInt32 argumentCount, Constructor& out) const {
            RS_ASSERT(constructors.size() != 0, "No constructors available");
            RS_ASSERT(argumentCount > 0 ? argumentTypes != 0 : true, "argumentTypes is null");
            Boolean result = false;
            for (RsSize i = 0; i < constructors.size(); ++i) {
                Constructor const& constructor = constructors.at(i); 
                RS_ASSERT(constructor.GetArgumentCount() == argumentCount, "constructor's argument count is incorrect"); 
                Boolean cont = false;
                for (RsSize j = 0; j < argumentCount; ++j) {
                    cont = !CompareArgumentTypes(constructor.GetRawArgumentType(j), *(argumentTypes + j));
                    if (cont) {
                        break;
                    }
                }
                
                if (cont) {
                    continue;
                }
                
                out = constructor;
                result = true;
                break;
            }
            
            return result;
        }
    }
}